//
//  MyRouter.swift
//  JCModule
//
//  Created by JC on 2023/3/16.
//

import Foundation
import UIKit
import Library_y

//MARK: - 路由
public class MyRouter: NSObject {
    
    // 单例方法
    public static let shared = MyRouter()
    
    /**
     错误通知：在跳转页面并且找不到对应的页面时会进行通知，监听该通知可以进行一些自定义操作，
                例如弹出错误页面。查看【AppDelegate.swift】文件了解错误监听。
     */
    public static let routerErrorNotificaiton = "RouterErrorNotificaiton"
    
    /**
     [scheme: [path: ViewController]]
     标识字典：通过模块标识获取对应的控制器
     */
    private lazy var vcDic = [String: [String: UIViewController.Type]]()
    
    /**
     对 url 进行解码
        
     - parameter url: url
     - returns: (scheme: 标识, path: 路径)?
     对 url 进行解码，我这里就分割了一下字符串，验证方式比较简单，实际可以做的更复杂些
     */
    private func decoudeUrl(_ url: String) -> (scheme: String, path: String)? {
        let urlAry = url.components(separatedBy: "://")
        guard urlAry.count >= 2 else {
            return nil
        }
        return (urlAry[0], urlAry[1])
    }
}

//MARK: - Public Action
public extension MyRouter {
    
    /**
     模块注册

     - 模块注册调用该方法
       
     - parameter moudle: 模块名称
     - parameter scheme: 标识
     - parameter pageClassName: 页面名称
     这里是模块注册部分，将对应模块的路径，标识进行存储
    */
    func registerModule(_ module: RouterMoudleProtocol.Type,
                        scheme: RouterMoudleProtocol.Type,
                        pathDic: [String: UIViewController.Type]) {
        if vcDic["\(scheme.self)"] == nil {
            vcDic["\(scheme.self)"] = [String: UIViewController.Type]()
        }
        vcDic["\(scheme)"] = pathDic
    }
    
    /**
     获取控制器
    
     - parameter url: 路由
     - parameter parameters: 传参
     - parameter callBackParameters: 目标参数回调
    
     - returns: 返回一个 UIViewController 控制器
     这里是获取控制器的部分，通过传入 url 配合参数和回调获取对应的控制器
    */
    func getViewController(_ url: String,
                           parameters: [String : Any]? = nil,
                           callBackParameters: (([String: Any]) -> Void)? = nil) -> UIViewController? {
        guard let decoude = decoudeUrl(url),
              let vcClass = vcDic[decoude.scheme]?[decoude.path] as? UIViewController.Type else {
            /** decoudeUrl() 方法先对 url 进行解码，
             如果解码失败，拿不到对应的 UIViewController.Type，则直接返回 nil
             */
            return nil
        }
        /**
         如果拿到了对应的 UIViewController.Type
         */
        return vcClass.routerController(parameters, callBackParameters: callBackParameters)
    }
    
    /**
     发送路由跳转错误通知
     当跳转页面时找不到对应页面，则会发送通知
     */
    func postRouterErrorNotification() {
        NotificationCenter.default.post(name: .init(MyRouter.routerErrorNotificaiton), object: nil, userInfo: nil)
    }
}

//MARK: - 跳转页面
extension UIViewController {
    
    /**
     返回当前的控制器
     可通过重写该方法，对传入的参数进行初始化，赋值等操作
        
     - 可重写：重写不需要调用 super 方法
        
     - parameter parameters: 可选参数
     - returns: 返回一个 UIViewController 控制器
     
     首先是返回当前的页面。

     当其他模块通过 url 获取当前页面时会调用该方法。
     默认情况是不需要传参直接创建一个自己页面的对象进行返回。

     如果当前页面需要传参才可以使用的话，可以重写该方法，
     然后对 parameters (传过来的参数进行操作)，然后再判断是否应该返回当前控制器
     */
    @objc open class func routerController(_ parameters: [String: Any]? = nil, callBackParameters: (([String: Any]) -> Void)? = nil) -> UIViewController? {
        // 返回一个控制器
        return self.init()
    }
    
    /**
     通过 url 获取目标控制器
        
     - parameter url: 路由
     - parameter parameters: 传参
     - parameter callBackParameters: 目标参数回调
        
     - returns: 返回一个 UIViewController 控制器
     
     有时会需要拿到一个其他模块的控制器，但是不需要跳转。
     可以通过该方法进行目标控制器的获取
     */
    @objc open func getViewController(_ url: String, parameters: [String : Any]? = nil, callBackParameters: (([String: Any]) -> Void)? = nil) -> UIViewController? {
        MyRouter.shared.getViewController(url, parameters: parameters, callBackParameters: callBackParameters)
    }
    
    /**
     返回到上一个页面
    
     返回到上一个页面，由于模块是分割开的，有些页面可能即支持 push 跳转，又支持 present 跳转，
     所以需要进行判断跳转方式再进行返回，
     在返回上一页时控制器直接调用该方法即可 【self.dismissRouterController(animated: true)】
    */
    @objc open func dismissRouterController(animated: Bool) {
        let children = self.navigationController?.children
        if children?.count ?? 0 > 1 && children?.last == self {
            self.navigationController?.popViewController(animated: animated)
        }else {
            self.dismiss(animated: animated, completion: nil)
        }
    }
   
    /**
     通过 url 的方式 present 一个控制器
    
     - 可重写：重写需要在最后调用 super 方法实现跳转
    
     - parameter url: 路由
     - parameter parameters: 可选参数
     - parameter animated: 是否执行动画
     - parameter callBackParameters: 目标参数回调
     */
    @objc open func presentRouterControllerWithUrl(_ url: String, parameters: [String: Any]? = nil, animated: Bool = true, callBackParameters: (([String: Any]) -> Void)? = nil) {
        presentRouterController(MyRouter.shared.getViewController(url, parameters: parameters, callBackParameters: callBackParameters), animated: animated)
    }
   
    /**
     通过 viewController 的方式 present 一个控制器
     (考虑到有些情况可能会先拿到控制器进行一些操作再跳转，所以可以使用该方法进行跳转)
    
     - 可重写：重写需要在最后调用 super 方法实现跳转
    
     - parameter viewController: target viewController
     - parameter animated: 是否执行动画
     */
    @objc open func presentRouterController(_ viewController: UIViewController?, animated: Bool = true) {
        guard let vc = viewController else {
            // 找不到控制器，发送错误通知
            MyRouter.shared.postRouterErrorNotification()
            return
        }
        
        present(vc, animated: animated, completion: nil)
    }
   
    /**
     通过 url 的方式 push 一个控制器, 需要带有 navigationController
    
     - 可重写：重写需要在最后调用 super 方法实现跳转
    
     - parameter url: 路由
     - parameter parameters: 可选参数
     - parameter animated: 是否执行动画
     - parameter callBackParameters: 目标参数回调
     */
    @objc open func pushRouterControllerWithUrl(_ url: String, parameters: [String: Any]? = nil, animated: Bool = true, callBackParameters: (([String: Any]) -> Void)? = nil) {
        pushRouterController(MyRouter.shared.getViewController(url, parameters: parameters, callBackParameters: callBackParameters), animated: animated)
    }
   
    /**
     通过 viewController 的方式 push 一个控制器, 需要带有 navigationController
    
     - 可重写：重写需要在最后调用 super 方法实现跳转
    
     - parameter viewController: target viewController
     - parameter animated: 是否执行动画
     */
    @objc open func pushRouterController(_ viewController: UIViewController?, animated: Bool = true) {
        guard let navigationController = self.navigationController else {
            // 找不到 navigationController 发送错误通知
            MyRouter.shared.postRouterErrorNotification()
            return
        }
        
        guard let vc = viewController else {
            // 找不到控制器，发送错误通知
            MyRouter.shared.postRouterErrorNotification()
            return
        }
        
        navigationController.pushViewController(vc, animated: animated)
    }
}
